

#include "FileStream.h"
#include "File.h"

#include <System/Int32.h>
#include <System/Diagnostics/Debug.h>
#include <System/NotImplementedException.h>
#include <System/FileNotFoundException.h>

#ifdef ref
#undef ref
#endif
#ifdef out
#undef out
#endif

#if Windows

// Reference: http://www.cprogramming.com/tutorial/cfileio.html

#else

#	include <stdio.h>
#	include <platforms/Setup.h>

#endif

using namespace Riccsson::System;
using namespace Riccsson::System::IO;
using namespace Riccsson::System::Diagnostics;

//---------------------------------------------------------------------------------------
FileStream::FileStream(string* path, FileMode mode)
	: _position(0)
	, _filesize(0)
{
	
#if Windows
	
	//r  - open for reading
	//w  - open for writing (file need not exist)
	//a  - open for appending (file need not exist)
	//r+ - open for reading and writing, start at beginning
	//w+ - open for reading and writing (overwrite file)
	//a+ - open for reading and writing (append if file exists)
	// Add 'b' at the end for binary files.
	switch(mode)
	{
	case FileMode::Open:
		{
			if (!File::Exists(path))
			{
				throw FileNotFoundException("");
			}
			_internalStream = fopen(path->c_str(), "rb");
		}
		break;
	case FileMode::Create:
		_internalStream = fopen(path->c_str(), "wb");
		break;
	default:
		throw NotImplementedException();
	}

	// Get filesize.
	// Seek to the end.
	fseek(_internalStream, 0L, SEEK_END);
	_filesize = ftell(_internalStream);

	// Get back.
	fseek(_internalStream, 0L, SEEK_SET);

#elif Android
	
	switch(mode)
	{
	case FileMode::Open:
		// Continue.
		break;
	default:
		throw NotImplementedException();
	}

	if(!mgr)
	{
		Debug::WriteLine("Assets MGR is NULL. ");
	}
	
	Debug::WriteLine("loading file with Assets.");

	const char* filename = path->c_str();
	AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_UNKNOWN);

	if(asset)
	{
		string message = string("AAssetManager_open. ") + filename;
		Debug::WriteLine(message);
	}
	else
	{
		string message = string("AAssetManager_open FAILED. ") + filename;
		Debug::WriteLine(message);
	}
	
	char buf[1024 * 256];
	int nb_read = 0;
	_internalVector.clear();
	while ((nb_read = AAsset_read(asset, buf, 1024 * 256)) > 0)
	{
		_internalVector.insert(_internalVector.end(), buf+0, buf + nb_read);
		_filesize += nb_read;

		Debug::WriteLine( string("Readed file size: ") + **Int32(_filesize).ToString());
	}
	Debug::WriteLine("AAsset_read.");
		
	AAsset_close(asset);

	Debug::WriteLine("Asset closed.");

#endif
}

//---------------------------------------------------------------------------------------
FileStream::~FileStream()
{
#if Windows
	Dispose(true);
#elif Android

#endif
}

//---------------------------------------------------------------------------------------
PROP3_GET_CPP(FileStream, long, Length)
{
	return _filesize;
}

//---------------------------------------------------------------------------------------
int FileStream::Read(Array<byte>* array, int offset, int count)
{
#if Windows
	
	fread((char*)(array->get() + offset), sizeof(char*), count, _internalStream);
	Position += count;
	return count;


#elif Android

	// TODO: Right now it is wrong. It doesnt cut to the end. It just now writes over the end.
	//fread((char*)array->get(), offset, count, (FILE*)_internalStream);
	for (int i = Position + offset; i < Position + offset + count; i++)
	{
		array->Insert(i, _internalVector.at(i));
	}
	Position += count;
	return count;
#endif
}

//---------------------------------------------------------------------------------------
int FileStream::Read(byte* array, int offset, int count)
{
#if Windows
	
	Debug::WriteLine("FileStream::Read():");
	Debug::WriteLine(Int32((int)offset).ToString());
	Debug::WriteLine(Int32((int)count).ToString());
	Debug::WriteLine(Int32((int)Position).ToString());
	Debug::WriteLine("FileStream::Read():  TEST");

	fread(array + offset, sizeof(byte), count, _internalStream);

	Position += count;
	return count;

#elif Android
	
	Debug::WriteLine("FileStream::Read(): Start");
	Debug::WriteLine(Int32((int)offset).ToString());
	Debug::WriteLine(Int32((int)count).ToString());
	Debug::WriteLine(Int32((int)Position).ToString());
	Debug::WriteLine("FileStream::Read():  TEST");

	//fread((char*)array, offset, count, (FILE*)_internalStream);
	for (int i = Position + offset; i < Position + offset + count; i++)
	{
		array[i] = _internalVector.at(i);
	}
	Debug::WriteLine("FileStream::Read():  Done");
	Position += count;
	return count;
#endif
}

//---------------------------------------------------------------------------------------
int FileStream::ReadByte()
{
#if Windows

	int c = fgetc(_internalStream);
	Position += 1;
	
	return c;

#elif Android
	int length(1);
	char c(0);
	c = _internalVector.at(Position);

	Position += 1;

	return (int)c;
#endif
}

//---------------------------------------------------------------------------------------
void FileStream::Write(Array<byte>* array, int offset, int count)
{

	if(offset + count > array->Length)
	{
		throw System::ArgumentException("Offset and count describe an invalid range in array.");
	}
	else if(offset < 0 || count < 0)
	{
		throw System::ArgumentOutOfRangeException("Offset or count is negative.");
	}

#if Windows

	fwrite(array->get() + offset, sizeof(byte), count, _internalStream);
	Position += count;

#elif Android

	int end = Position + count;
	for (int i = Position; i < end; i++)
	{
		_internalVector[i] = (*array)[offset + i];
	}

#endif
}

//---------------------------------------------------------------------------------------
void FileStream::Dispose(bool disposing)
{
#if Windows
	if (_internalStream != null)
	{
		fclose(_internalStream);
		_internalStream = null;
	}
#elif Android
	
	throw System::NotImplementedException();

#endif
}